#include"myshell.h"
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<stdlib.h>
#include<string>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

//命令行参数表，故意定为全局
char *gargv[ARGS] = {NULL};
int gargc = 0;
char pwd[1024];//全局变量空间，保存当前shell进程的工作路径
int lastcode = 0;

void Debug()
{
  printf("hello shell!\n");
}

//利用环境变量来获取主机名，用户名
//并且我不想将这几个函数暴露给外部，这样只能在本文件中使用
//当 static 修饰函数时，该函数的作用域会被限制在定义它的文件内，其他文件无法调用该函数。
static std::string GetUserName()
{
  std::string username = getenv("USER");
  return username.empty() ? "None" : username;
}

static std::string GetHosname()
{
  std::string hostname = getenv("HOSTNAME");
  return hostname.empty() ? "None" : hostname;
}

static std::string GetPwd()
{
  // 环境变量的变化，可能会依赖于进程,pwd需要shell自己更新环境变量的值
  char temp[1024];
  //temp指向字符数组的指针，用于存放获利取到当前工作目录的路径
  getcwd(temp, sizeof(temp));
  //顺便更新一下shell自己的环境变量pwd
  //snprintf用于格式化字符串并将其写入缓冲区
  //@1指向目标缓冲区的指针，格式化后的字符串将被写入到这个缓冲区。
  //@2缓冲区大小； @3格式化字符串，规定如何对后续的参数进行格式化，如：%s表示格式化字符串
  //@4...可变参数列表，这些参数会根据format指定的格式进行格式化
  snprintf(pwd, sizeof(pwd), "PWD=%s", temp);
  //用于设置环境变量的函数，将 string 指定的环境变量添加到当前进程的环境变量列表中
  putenv(pwd);

  //  /home/lzs/linux/myshell
  std::string pwd_lable = temp;
  const std::string pathsep = "/";
  auto pos = pwd_lable.rfind(pathsep);
  if(pos == std::string::npos)
  {
    return "None";
  }

  //substr用于返回字符串的子串。
  pwd_lable = pwd_lable.substr(pos+pathsep.size());
  return pwd_lable.empty() ? "/" : pwd_lable;
}

static std::string GetHomePath() 
{
  std::string home = getenv("HOME");
  return home.empty() ? "/" : home;
}

//输出提示符
void PrintCommandPrompt()
{
  std::string user = GetUserName();
  std::string hostname = GetHosname();
  std::string pwd = GetPwd();
  //c_str()将string对象转化为C风格的字符串(即以空字符 '\0' 结尾的字符数组)
  printf("[%s@%s %s]# ", user.c_str(), hostname.c_str(), pwd.c_str());
}

//获取用户的键盘输入
bool GetCommandString(char cmd_str_buff[], int len)
{
  if(cmd_str_buff == NULL || len <= 0)
    return false;
  char *res = fgets(cmd_str_buff, len, stdin);
  if(res == NULL)
    return false;
  // ls -a -l\n -> ls -a -l\0
  // 把\n变为\0
  // cmd_str_buff不可能为0，因为我们至少要按一下回车，它最少是1
  cmd_str_buff[strlen(cmd_str_buff) - 1] = 0;
  return strlen(cmd_str_buff) == 0 ? false : true;
}

bool ParseCommandString(char cmd[])
{
  if(cmd == NULL)
    return false;
#define SEP " "
  //"ls -a -l" -> "ls" "-a" "-l"
  gargv[gargc++] = strtok(cmd, SEP);
  //整个数字，最后以NULL结尾
  //while((bool)(gargv[gargc++])) = strtok(NULL, SEP);
  //strtok分隔子串，第一个参数为NULL时会向后推进
  while((bool)(gargv[gargc++] = strtok(NULL, SEP)));
  //回退一次，命令行参数的格式
  gargc--;

//#define DEBUG
#ifdef DEBUG
  //printf("gargc: %d\", gargc);
  printf("-------------------------\n");
  for(int i = 0; i < gargc; i++)
  {
    printf("gargc[%d]: %s\n", i, gargv[i]);
  }
  printf("--------------------------\n");
  for(int i = 0; gargv[i]; i++)
  {
      printf("gargv[%d]: %s\n", i, gargv[i]);
  } 
#endif

  return true;
}

void InitGlobal()
{
  gargc = 0;
  //函数会将从 s 开始的连续 n 个字节的内存区域设置为 c 的低 8 位值。
  //这通常用于初始化内存,清空数组或设置特定的内存模式。
  memset(gargv, 0, sizeof(gargv));
}

void ForkAndExec()
{
  pid_t id = fork();
  if(id < 0)
  {
    perror("fork");
    return;
  }
  else if(id == 0)
  {
    //子进程
    execvp(gargv[0], gargv);
    exit(0);
  }
  else
  {
    //父进程
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
      lastcode = WEXITSTATUS(status);
    }
  }
}

bool BuiltInCommandExec()
{
  //内建命令：是shell自己执行的命令，如同shell执行一个自己的函数
  //gargv[0];
  std::string cmd = gargv[0];
  bool ret = false;
  if(cmd == "cd")
  {
    // build
    if(gargc == 2)
    {
      std::string target = gargv[1];
      if(target == "~")
      {
        ret = true;
        chdir(gargv[1]);
      }

    }
    else if(gargc == 1)
    {
      ret = true;
      chdir(GetHomePath().c_str());
    }
    else{
      //
    }
  }
  else if(cmd == "echo")
  {
    if(gargc == 2)
    {
      std::string args = gargv[1];
      if(args[0] == '$')
      {
        if(args[1] == '?')
        {
          printf("lastcode: %d\n", lastcode);
          lastcode = 0;
          ret = true;
        }
        else{
          const char *name = &args[1];
          printf("%s\n", getenv(name));
          lastcode = 0;
          ret = true;
        }
      }
      else {
        printf("%s\n", args.c_str());
        ret = true;
      }
    }
  }
  return ret;
}




